package com.teradata.feedProc;

import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import java.text.ParseException;
import java.text.SimpleDateFormat;

import com.teradata.feedProc.beans.FeedInfo;
import com.teradata.feedProc.beans.FieldInfo;
import com.teradata.feedProc.enums.FileFormatType;

/**
 * Implementation of IFileLine.
 */
public class FileLine implements IFileLine {
	private String line = null;
	private final FeedInfo feedInfo;
	private final List<FieldInfo> fieldInfoList;

	// Constructor
	public FileLine(final String string, final FeedInfo feedInfo, final List<FieldInfo> fieldInfoList) {
		this.line = string;
		this.feedInfo = feedInfo;
		this.fieldInfoList = fieldInfoList;
	}

	/**
	 * fileCheck will compare the length of the record that is read in to the
	 * size that is taken from the query. If they match, the record is good. If
	 * they differ, the information will be considered bad and the program will
	 * quit
	 * 
	 * @param ID
	 *            Will be true if the file will need the dlz_rec_id and feedkey
	 *            added
	 * @return Will return true if the current record being read contains a date
	 *         field
	 */
	public void fileCheck(boolean ID) {

		if (feedInfo.getFormatType().equals(FileFormatType.D)) {
			int fieldCount = 0;
			int lineSize;
			// Splits the record by delimiters, ignoring delimiter characters
			// within fields.
			String fields[] = line.split(feedInfo.getDelimiterType().getValue());
			lineSize = fields.length;
			
			for (FieldInfo fieldInfo : fieldInfoList) {
				fieldCount++;
			}

			if (ID) {
				lineSize += 2;
			}

			// If the number of columns does not match the expected number from
			// MDF, exit the program.
			System.out.println("lineSize = " + lineSize + " --- fieldCount = " + fieldCount);
			
			
			if (lineSize != fieldCount) {
				System.out.println("MDF does not match record");
				System.exit(1);
			}
		} else {
			int totalSize = 0, dlz_recSize = 0, feed_keySize = 0, lineSize = 0;
			lineSize = line.length();

			for (FieldInfo fieldInfo : fieldInfoList) {
				totalSize += fieldInfo.getiFieldLength();

				if (fieldInfo.getStrFieldName().equalsIgnoreCase("dlz_rec_id")) {
					dlz_recSize = fieldInfo.getiFieldLength();
				}
				if (fieldInfo.getStrFieldName().equalsIgnoreCase("feed_key")) {
					feed_keySize = fieldInfo.getiFieldLength();
				}
			}

			if (ID) {
				lineSize += dlz_recSize + feed_keySize;
			}

			// If the size of the record does not match the expected size from
			// MDF, exit the program.
			if (lineSize != totalSize) {
				System.out.println("MDF does not match record");
				System.exit(1);
			}
		}
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @param conn
	 */
	@Override
	public List<String> getDates(Boolean ID, String quoted, String feedkey, int iLineCnt, String dbURL, String userID, String password)
			throws Exception {
		List<String> dates = new ArrayList<String>();
		String returnLine = line;
		int dlz_index = -1;
		int feedkey_index = -1;
		// If DELIMITED, then use delimiter type to split line
		if (feedInfo.getFormatType().equals(FileFormatType.D)) {
			String fields[] = line.split(feedInfo.getDelimiterType().getValue().concat("(?=([^\"]*\"[^\"]*\")*[^\"]*$)"));

			if (fields.length != 0) {
				for (FieldInfo fieldInfo : fieldInfoList) {
					// if the command line arguments contained a feedkey, add
					// the feedkey and dlz_rec_id columns to the record

					if (ID) {
						if (fieldInfo.getStrFieldName().equalsIgnoreCase("dlz_rec_id")) {
							dlz_index = fieldInfo.getiFieldIndex();

							returnLine = reconstruct(feedInfo, fields, dlz_index, quoted + Integer.toString(iLineCnt) + quoted);

							fields = returnLine.substring(0, returnLine.length() - 1).split(feedInfo.getDelimiterType().getValue().concat("(?=([^\"]*\"[^\"]*\")*[^\"]*$)"));
						}
						if (fieldInfo.getStrFieldName().contains("feed_key")) {
							feedkey_index = fieldInfo.getiFieldIndex();

							returnLine = reconstruct(feedInfo, fields, feedkey_index, quoted + feedkey + quoted);

							fields = returnLine.substring(0, returnLine.length() - 1).split(feedInfo.getDelimiterType().getValue().concat("(?=([^\"]*\"[^\"]*\")*[^\"]*$)"));
						}
					}

					// if the column contains a date, perform the date
					// checks/formatting
					if (fieldInfo.isbIsDateType()) {
						if (fields.length <= (fieldInfo.getiFieldIndex() - 1)) {
							System.out.println("Record number " + iLineCnt + " does not match. Exiting.");
							System.exit(1);
						}
						returnLine = formatDelimited(returnLine, feedInfo.isbIsQuoted(), quoted, feedInfo.getDelimiterType().getValue(), fieldInfo, dates, iLineCnt);
					}
				}
				dates.add(returnLine);
			}
		} else {
			// FIXED
			int startIndex = 0;
			for (FieldInfo fieldInfo : fieldInfoList) {
				if (startIndex <= returnLine.length()) {
					// if the command line arguments contained a feedkey, add
					// the feedkey and dlz_rec_id columns to the record
					if (ID) {
						if (fieldInfo.getStrFieldName().equalsIgnoreCase("dlz_rec_id")) {
							returnLine = returnLine.substring(0, startIndex) + String.format("%1$" + fieldInfo.getiFieldLength() + "s", iLineCnt)
									+ returnLine.substring(startIndex);
						}
						if (fieldInfo.getStrFieldName().equalsIgnoreCase("feed_key")) {
							returnLine = returnLine.substring(0, startIndex) + String.format("%1$" + fieldInfo.getiFieldLength() + "s", Integer.parseInt(feedkey))
									+ returnLine.substring(startIndex);
						}
					}
					// if a field name is '_clear', add a column to the record
					// at that position

					String fieldValue = returnLine.substring(startIndex, startIndex + fieldInfo.getiFieldLength());
					if (fieldInfo.isbIsDateType()) {
						returnLine = formatFixed(returnLine, fieldValue, fieldInfo, startIndex, dates, iLineCnt);
					}
					// Finds the next column by adding the column size to the
					// current position
					startIndex += fieldInfo.getiFieldLength();
				}
			}
			dates.add(returnLine);
		}

		return dates;
	}

	/**
	 * Reconstructs the record after changing the value of one of the fields
	 * 
	 * @param feedInfo
	 * @param fields
	 * @param index
	 * @param toAdd
	 * @return returnLine
	 */
	public String reconstruct(FeedInfo feedInfo, String[] fields, int index, String toAdd) {
		String returnLine = "";

		if (index == 1) {
			if (feedInfo.getDelimiterType().getValue().equals("\\|")) {
				returnLine += toAdd + "|";
			} else if (feedInfo.getDelimiterType().getValue().equals("\\x01")) {
				returnLine += toAdd + Character.toString((char) 0x1);
			} else if (feedInfo.getDelimiterType().getValue().equals("\\x09")) {

				returnLine += toAdd + Character.toString((char) 0x9);
			} else if (feedInfo.getDelimiterType().getValue().equals("\\x20")) {

				returnLine += toAdd + Character.toString((char) 0x20);
			} else {
				returnLine += toAdd + feedInfo.getDelimiterType().getValue();
			}
		}

		for (int i = 0; i < fields.length; i++) {
			// specify certain delimiters to write back when reconstructing the
			// record; Some characters do not print normally.
			if (feedInfo.getDelimiterType().getValue().equals("\\|")) {
				if (index - 2 == i) {
					returnLine += fields[i] + "|" + toAdd + "|";
				} else {
					returnLine += fields[i] + "|";
				}
			} else if (feedInfo.getDelimiterType().getValue().equals("\\x01")) {
				if (index - 2 == i) {
					returnLine += fields[i] + Character.toString((char) 0x1) + toAdd + Character.toString((char) 0x1);
				} else {
					returnLine += fields[i] + Character.toString((char) 0x1);
				}
			} else if (feedInfo.getDelimiterType().getValue().equals("\\x09")) {
				if (index - 2 == i) {
					returnLine += fields[i] + Character.toString((char) 0x9) + toAdd + Character.toString((char) 0x9);
				} else {
					returnLine += fields[i] + Character.toString((char) 0x9);
				}
			} else if (feedInfo.getDelimiterType().getValue().equals("\\x20")) {
				if (index - 2 == i) {
					returnLine += fields[i] + Character.toString((char) 0x20) + toAdd + Character.toString((char) 0x20);
				} else {
					returnLine += fields[i] + Character.toString((char) 0x20);
				}
			} else {
				if (index - 2 == i) {
					returnLine += fields[i] + feedInfo.getDelimiterType().getValue() + toAdd + feedInfo.getDelimiterType().getValue();
				} else {
					returnLine += fields[i] + feedInfo.getDelimiterType().getValue();
				}
			}
		}
		return returnLine;
	}

	public String formatFixed(String line, String date, FieldInfo fieldInfo, int startIndex, List<String> dates, int iLineCnt) {
		String formattedDate = "", unChanged = date, transformedDate = "", append = "";
		String dateForm = fieldInfo.getStrFormat(false), dateForm2 = dateForm, origForm = "";
		int count = 0, index = 0;
		boolean sameLength = true, hasQ = false;
		Date myDate = null;

		// if the date given is neither a null or blank, go through with the
		// date transformation
		if (unChanged != null && !unChanged.trim().equals("")) {

			// if the date is not considered an exception, set transformedDate
			// to the value pulled
			// otherwise, set append to the value that is removed from the
			// exception date
			if ((transformedDate = fieldInfo.transformValueForFormat(unChanged)) == null) {
				transformedDate = unChanged.trim();
			} else {
				origForm = fieldInfo.getStrFormat(true);
				append = unChanged.substring(transformedDate.length(), unChanged.length());
			}

			// if the length of the date given is not equal to the length of the
			// date format, invalidate the date

			if (!origForm.equals("")) {
				if (transformedDate.length() + append.length() != origForm.length() && transformedDate.trim().length() != 0) {
					sameLength = false;
				}
			} else {
				if (transformedDate.length() != dateForm.length() && transformedDate.length() != 0) {
					sameLength = false;
				}
			}

			// count how many year digits are in the format
			for (int j = dateForm.length() - 1; j >= 0; j--) {
				if (dateForm.charAt(j) == 'y') {
					index = j;
					count++;
				}

				// if the date format contains a '?', ignore the date length
				if (dateForm.charAt(j) == '?') {
					String sub1 = dateForm.substring(0, j);
					String sub2 = dateForm.substring(j + 1);
					dateForm = sub1 + dateForm.charAt(j - 1) + sub2;
					hasQ = true;
					dateForm2 = dateForm;
				}
			}

			// If there are less than 4 year digits, add the centuries to be
			// printed
			if (count < 3 && count > 0) {
				// Format the date in case there are any spaces preceding the
				// date
				// This will replace the space with a '0' and allow for the
				// centuries to be added correctly
				SimpleDateFormat format = new SimpleDateFormat(dateForm);
				Date tempDate;
				try {
					tempDate = format.parse(transformedDate);
					transformedDate = format.format(tempDate);
				} catch (ParseException e) {
					sameLength = false;
				}
				String year = transformedDate.substring(index, index + 2);

				// if the last 2 digits of the year are less than 50, add '20'
				// as the century
				// otherwise, set '19' as the century
				if (Integer.parseInt(year) < 50) {
					String part1 = transformedDate.substring(0, index);
					String part2 = transformedDate.substring(index);
					transformedDate = part1 + "20" + part2;
				} else {
					String part1 = transformedDate.substring(0, index);
					String part2 = transformedDate.substring(index);
					transformedDate = part1 + "19" + part2;
				}

				// make the format the date is printing out to contain 4 year
				// digits
				dateForm2 = dateForm.substring(0, index) + "yy" + dateForm.substring(index);
			}
		}
		try {
			// if the lengths did not match, and the format did not contain a
			// '?'
			// it is a bad value
			if (!sameLength && !hasQ) {
				throw new IllegalArgumentException("Date does not match the format length");
			}

			String part1 = line.substring(0, startIndex);
			String part2 = line.substring(startIndex + fieldInfo.getiFieldLength());
			SimpleDateFormat format2 = new SimpleDateFormat(dateForm2);
			format2.setLenient(false);

			// only check if there is a value for date; nulls/blanks are allowed
			if (transformedDate.trim() != null && !transformedDate.trim().equals("")) {
				myDate = format2.parse(transformedDate);
				formattedDate = part1 + String.format("%1$" + fieldInfo.getiFieldLength() + "s", format2.format(myDate) + append) + part2;
			} else {
				formattedDate = part1 + String.format("%1$" + fieldInfo.getiFieldLength() + "s", unChanged) + part2;
			}
		} catch (Exception e) {
			if (date.length() > 2) {
				// Append 'BAD' to the beginning of the line-to-print to
				// distinguish where to print it to
				dates.add("BAD" + iLineCnt + ", " + fieldInfo.getStrFieldName() + ": " + date.trim());
				if (!fieldInfo.getStrDefaultVal().equals("null")) {
					date = fieldInfo.getStrDefaultVal();
				}
				String part1 = line.substring(0, startIndex);
				String part2 = line.substring(startIndex + fieldInfo.getiFieldLength());

				// set the date to print to the record as the default value
				formattedDate = part1 + String.format("%1$" + fieldInfo.getiFieldLength() + "s", date) + part2;
			}
		}
		return formattedDate;
	}

	public String formatDelimited(String line, boolean bIsQuoted, String quoted, String delimiter, FieldInfo fieldInfo, List<String> dates, int dlz_index) {
		String fields[] = line.split(feedInfo.getDelimiterType().getValue().concat("(?=([^\"]*\"[^\"]*\")*[^\"]*$)"));
		String formattedDate = "", unChanged = fields[fieldInfo.getiFieldIndex() - 1], transformedDate = "", append = "";
		String dateForm = fieldInfo.getStrFormat(false), dateForm2 = dateForm, origForm = "";
		boolean sameLength = true, hasQ = false;
		int count = 0, index = 0;
		Date myDate = null;

		// if the record contains quoted strings, remove the '"' or "'".
		if (bIsQuoted) {
			unChanged = fields[fieldInfo.getiFieldIndex() - 1].replaceAll(quoted, "");
		}
		unChanged = unChanged.trim();
		// if the date given is neither a null or blank, go through with the
		// date transformation
		if (unChanged != null && !unChanged.equals("")) {

			// if the date is not considered an exception, set transformedDate
			// to the value pulled
			// otherwise, set append to the value that is removed from the
			// exception date
			if ((transformedDate = fieldInfo.transformValueForFormat(unChanged)) == null) {
				transformedDate = unChanged;
			} else {
				origForm = fieldInfo.getStrFormat(true);
				append = unChanged.substring(transformedDate.length(), unChanged.length());
			}

			// if the column contains any blanks after the date, remove all of
			// them
			while (transformedDate.endsWith(" ")) {
				transformedDate = transformedDate.substring(0, transformedDate.length() - 1);
			}

			// if the length of the date is not the same as the format,
			// invalidate
			// the date
			if (!origForm.equals("")) {
				if (transformedDate.length() + append.length() != origForm.length() && transformedDate.trim().length() != 0) {
					// If the lengths are not the same, invalidate the date
					sameLength = false;
				}
			} else {
				if (transformedDate.length() != dateForm.length() && transformedDate.trim().length() != 0) {
					sameLength = false;
				}
			}

			// count how many year digits are in the format
			for (int j = dateForm.length() - 1; j >= 0; j--) {
				if (dateForm.charAt(j) == 'y') {
					index = j;
					count++;
				}

				// if the date format contains a '?', ignore the date length
				if (dateForm.charAt(j) == '?') {
					String sub1 = dateForm.substring(0, j);
					String sub2 = dateForm.substring(j + 1);
					dateForm = sub1 + dateForm.charAt(j - 1) + sub2;
					hasQ = true;
					dateForm2 = dateForm;
				}
			}

			// if there are less than 4 year digits, add the century digits to
			// be printed
			if (count < 3 && count > 0) {
				// Format the date in case there are any spaces preceding the
				// date
				// This will replace the space with a '0' and allow for the
				// centuries to be added correctly
				SimpleDateFormat format = new SimpleDateFormat(dateForm);
				Date tempDate;
				try {
					tempDate = format.parse(transformedDate);
					transformedDate = format.format(tempDate);
				} catch (ParseException e) {
					sameLength = false;
				}
				String year = transformedDate.substring(index, index + 2);

				// if the last 2 digits of the year are less than 50, add '20'
				// as the century
				// otherwise, set '19' as the century
				if (Integer.parseInt(year) < 50) {
					String part1 = transformedDate.substring(0, index);
					String part2 = transformedDate.substring(index);
					transformedDate = part1 + "20" + part2;
				} else {
					String part1 = transformedDate.substring(0, index);
					String part2 = transformedDate.substring(index);
					transformedDate = part1 + "19" + part2;
				}

				// make the format the date is printing out to contain 4 year
				// digits
				dateForm2 = dateForm.substring(0, index) + "yy" + dateForm.substring(index);
			}
		}

		// if the lengths did not match, and the format did not contain a '?'
		// it is a bad value
		try {
			if (!sameLength && !hasQ) {
				throw new IllegalArgumentException("Date does not match the format length");
			}
			SimpleDateFormat format2 = new SimpleDateFormat(dateForm2);
			format2.setLenient(false);

			if (transformedDate != null && !transformedDate.trim().equals("")) {
				myDate = format2.parse(transformedDate);

				for (int i = 0; i < fields.length; i++) {
					if (delimiter.equals("\\|")) {
						if (i == fieldInfo.getiFieldIndex() - 1) {
							formattedDate += quoted + format2.format(myDate) + append + quoted + "|";
						} else {
							formattedDate += fields[i] + "|";
						}
					} else if (delimiter.equals("\\x01")) {
						if (i == fieldInfo.getiFieldIndex() - 1) {
							formattedDate += quoted + format2.format(myDate) + append + quoted + Character.toString((char) 0x1);
						} else {
							formattedDate += fields[i] + Character.toString((char) 0x1);
						}
					} else if (delimiter.equals("\\x09")) {
						if (i == fieldInfo.getiFieldIndex() - 1) {
							formattedDate += quoted + format2.format(myDate) + append + quoted + Character.toString((char) 0x9);
						} else {
							formattedDate += fields[i] + Character.toString((char) 0x9);
						}
					} else if (delimiter.equals("\\x20")) {
						if (i == fieldInfo.getiFieldIndex() - 1) {
							formattedDate += quoted + format2.format(myDate) + append + quoted + Character.toString((char) 0x20);
						} else {
							formattedDate += fields[i] + Character.toString((char) 0x20);
						}
					} else {
						if (i == fieldInfo.getiFieldIndex() - 1) {
							formattedDate += quoted + format2.format(myDate) + append + quoted + delimiter;
						} else {
							formattedDate += fields[i] + delimiter;
						}
					}
				}
			} else {
				for (int i = 0; i < fields.length; i++) {
					if (delimiter.equals("\\|")) {
						if (i == fieldInfo.getiFieldIndex() - 1) {
							formattedDate += quoted + unChanged + quoted + "|";
						} else {
							formattedDate += fields[i] + "|";
						}
					} else if (delimiter.equals("\\x01")) {
						if (i == fieldInfo.getiFieldIndex() - 1) {
							formattedDate += quoted + unChanged + quoted + Character.toString((char) 0x1);
						} else {
							formattedDate += fields[i] + Character.toString((char) 0x1);
						}
					} else if (delimiter.equals("\\x09")) {
						if (i == fieldInfo.getiFieldIndex() - 1) {
							formattedDate += quoted + unChanged + quoted + Character.toString((char) 0x9);
						} else {
							formattedDate += fields[i] + Character.toString((char) 0x9);
						}
					} else if (delimiter.equals("\\x20")) {
						if (i == fieldInfo.getiFieldIndex() - 1) {
							formattedDate += quoted + unChanged + quoted + Character.toString((char) 0x20);
						} else {
							formattedDate += fields[i] + Character.toString((char) 0x20);
						}
					} else {
						if (i == fieldInfo.getiFieldIndex() - 1) {
							formattedDate += quoted + unChanged + quoted + delimiter;
						} else {
							formattedDate += fields[i] + delimiter;
						}
					}
				}
			}
		} catch (Exception e) {
			dates.add("BAD" + dlz_index + ", " + fieldInfo.getStrFieldName().trim() + ": " + fields[fieldInfo.getiFieldIndex() - 1].trim());
			if (!fieldInfo.getStrDefaultVal().equals("null")) {
				fields[fieldInfo.getiFieldIndex() - 1] = quoted + fieldInfo.getStrDefaultVal() + quoted;
			}
			for (int i = 0; i < fields.length; i++) {
				if (delimiter.equals("\\|")) {
					formattedDate += fields[i] + "|";
				} else if (delimiter.equals("\\x01")) {
					formattedDate += fields[i] + Character.toString((char) 0x1);
				} else if (delimiter.equals("\\x09")) {
					formattedDate += fields[i] + Character.toString((char) 0x9);
				} else if (delimiter.equals("\\x20")) {
					formattedDate += fields[i] + Character.toString((char) 0x20);
				} else {
					formattedDate += fields[i] + delimiter;
				}
			}
		}

		return formattedDate;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getLineText() {
		return line;
	}
}